//******************************************************************
//******************************************************************
//**********          ANts Peer To Peer Sources        *************
//
// ANts P2P realizes a third generation P2P net. It protects your
// privacy while you are connected and makes you not trackable, hiding
// your identity (ip) and crypting everything you are sending/receiving
// from others.

// Copyright (C) 2004  Roberto Rossi

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

package ants.p2p.security.sockets;

import java.net.*;
import java.io.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;

import kerjodando.vincent.Common;

import ants.p2p.*;

import org.apache.log4j.*;

public class SecureServerSocketThread {
	public static final String cipher = "AES";

	public static final int cipherKeySize = 16;

	public static final int DHKeyExchangeBits = 512;

	static Logger _logger = Logger.getLogger(SecureServerSocketThread.class
			.getName());

	DHParameterSpec dhParamSpec;

	KeyPair serverKpair;

	KeyAgreement serverKeyAgree;

	PublicKey serverPubKey;

	PublicKey clientPubKey;

	byte[] sharedSecret;

	byte[] aesKey;

	Cipher serverCipherEnc;

	Cipher serverCipherDec;

	int remoteServerPort;

	Socket localSocket;

	InputStream socketInputStream;

	OutputStream socketOutputStream;

	CipherInputStream cipherSocketInputStream;

	CipherOutputStream cipherSocketOutputStream;

	boolean newVersionDetected = false;

	String newerVersion = Ant.getVersion();

	String sURI = null;

	long timeElapsed;

	int pingSize = 32;

	public SecureServerSocketThread(Socket localSocket) throws IOException,
			NoSuchAlgorithmException, InvalidParameterSpecException,
			InvalidAlgorithmParameterException, InvalidKeyException {
		this.localSocket = localSocket;
		this.localSocket.setSoTimeout(60000);
		this.run();
		if (!this.localSocket.isClosed())
			this.localSocket.setSoTimeout(3 * 60000);
	}

	public void run() {
		try {
			/*
			 * this.generateDHParameters(); this.serverCipherEnc =
			 * Cipher.getInstance(SecureServerSocketThread.cipher);
			 * this.serverCipherDec =
			 * Cipher.getInstance(SecureServerSocketThread.cipher);
			 */

			this.socketInputStream = localSocket.getInputStream();
			this.socketOutputStream = localSocket.getOutputStream();

			// output my information
			ObjectSocketOutputStream oos = new ObjectSocketOutputStream(
					this.socketOutputStream);
			oos.writeObject(Ant.getProtocolVersion());
			oos.writeObject(Ant.getApplicationName());
			oos.writeObject(Ant.getVersion());
			oos.flush();

			// verify other peer's information
			ObjectSocketInputStream osi = new ObjectSocketInputStream(
					this.socketInputStream);
			String protocolVersion = (String) osi.readObject();
			if (!protocolVersion.equals(Ant.getProtocolVersion())) {
				if (!protocolVersion.startsWith("beta")
						&& protocolVersion.compareTo(Ant.getProtocolVersion()) > 0) {
					this.newVersionDetected = true;
					this.newerVersion = "Protocol update: " + protocolVersion;
				}
				this.localSocket.close();
				return;
			}
			String applicationName = (String) osi.readObject();
			String version = (String) osi.readObject();
			boolean isKerjordando = applicationName.equals(Ant
					.getApplicationName());
			if (isKerjordando) {
				if (!version.equals(Ant.getVersion())) {
					if (version.compareTo(Ant.getVersion()) > 0) {
						this.newVersionDetected = true;
						this.newerVersion = version;
					}
				}
			}

			this.remoteServerPort = osi.readInt();

			long startProbe = System.currentTimeMillis();
			oos.writeObject(new byte[pingSize]);
			if (isKerjordando)
				oos.writeObject(Common.identifier_id);
			oos.flush();
			osi.readObject();
			timeElapsed = (System.currentTimeMillis() - startProbe) / 2;
			if (isKerjordando)
				this.sURI = (String) osi.readObject();

		} catch (Exception e) {
			try {
				this.localSocket.close();
			} catch (Exception ex) {
			}
			_logger.info("Secure server connection failed with: "
					+ localSocket.getInetAddress() + ":"
					+ localSocket.getPort() + " " + e.getMessage(), e);
			_logger.debug("Secure server connection failed", e);
		}
	}

	public String getURI() {
		return this.sURI;
	}

	public long getTimeElapsed() {
		return this.timeElapsed;
	}

	public Cipher getCipherEnc() {
		return this.serverCipherEnc;
	}

	public Cipher getCipherDec() {
		return this.serverCipherDec;
	}

	public Socket getSocket() {
		return this.localSocket;
	}

	public int getRemoteServerPort() {
		return this.remoteServerPort;
	}

	public boolean isNewVersionDetected() {
		return this.newVersionDetected;
	}

	public String getNewerVersion() {
		return this.newerVersion;
	}
}
